package com.bshinfo.web.base.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import sun.misc.BASE64Encoder;

import com.alibaba.fastjson.JSON;


/**
 * 资源中心Http请求客户端API<br/>
 * 
 * 功能描述: 对资源中心进行Http get post 请求<BR/>
 * 
 * < <BR />
 * 	 1.  <BR/>
 *   2.  <BR/>
 *   3.  <BR/>
 * >
 * 
 * 
 * @author 	[zhangChao]
 * 
 * @see 	[com.bshinfo.web.base.util.HttpUtil]
 * 
 * @since 	[JDK 1.7] 
 * 
 * @version [1.0]
 * 
 * @Date    [2017-1-22 11:25:43]
 */
public class ResourceRestAPIClient 
{
	/**私有全局变量配置*/
	
	// 公钥
	private String accessKey;
	
	// 私钥
	private String secretKey;
	
	// 请求地址
	private String serverHost;
	
	private String apiPath = "/api/v1";
	
	private String endpoint;
	
	private int  lifetime = 60000;
	
	
	public ResourceRestAPIClient(String accessKey, String secretKey, String serverHost, int lifetime)
	{
		this.accessKey = accessKey;
		this.secretKey = secretKey;
		this.serverHost = serverHost;
		this.lifetime = lifetime;
		
		this.endpoint = this.serverHost + this.apiPath;
	}
	
	public ResourceRestAPIClient(String accessKey, String secretKey, String serverHost)
	{
		this(accessKey, secretKey, serverHost, 60000);
	}
	
	public String getResCategories() throws IOException
	{
		String uri = "/res_categories";
		return doGet(uri);
	}
	
	public String doGet(String uri) throws IOException
	{
		return doGet(uri, null, null);
	}
	
	public String doGet(String uri, Map<String, String> headers) throws IOException
	{
		return doGet(uri, null, headers);
	}
	
	public String  getResCataoriesByPOST() throws IOException
	{
		return doPost("/res_categories",null,null);
	}
	
	/**
	 * Get请求方式
	 * 
	 * @param uri		请求地址
	 * 
	 * @param params    请求参数
	 * 
	 * @param headers   请求头[request-header]
	 * 
	 * @return {String}
	 * @throws IOException 
	 */
	public String doGet(String uri, Map<String, String> params, Map<String, String> headers) throws IOException 
	{
		if (null != params ) 
		{
			uri = uri + (uri.indexOf("?") > 0 ? "&" : "?") + httpBuildQuery(params);
		}
		
		String requestId = this.makeRequestId();
		
		// 拼接 uri固定前缀
		String url = this.makeUrl(uri);
		
		// 生成当前请求token
		String token = this.packToken(this.makeSignatureUri(uri), null, (System.currentTimeMillis() + this.lifetime)+"",requestId);
		
		// 请求头设置
		if (null == headers) 
		{
			headers = new HashMap<String ,String >();
		}
		
		headers.putAll(getHeaders(token, requestId));
		
		net.sf.json.JSONObject json = HttpUtil.getInstance().getJsonExecute(url, headers, null, "GET", this);
		
		return json==null?"":json.toString();
	}
	

	public String doPost(String uri, Map<String, String> params, Map<String, String> headers) throws IOException 
	{
		String requestId = this.makeRequestId();
		String url = this.makeUrl(uri);
		String data = JSON.toJSONString(params);
		String token = this.packToken(this.makeSignatureUri(uri), data, (System.currentTimeMillis() + this.lifetime)+"",requestId);
		if (null == headers)
		{
			headers = new HashMap<String ,String >();
		}
		headers.putAll(getHeaders(token, requestId));
		
		net.sf.json.JSONObject json = HttpUtil.getInstance().getJsonExecute(url, headers, null, "POST", this);
		
		
		return json==null?"":json.toString();
		//return HttpClientUtil.getInstance().sendPost(url,data, headers);
	}

	private Map<String, String> getHeaders(String token, String requestId) 
	{
		Map<String, String> headers = new HashMap<String, String>();
		headers.put("Content-type", "application/json");
		headers.put("X-Auth-Token", token);
		headers.put("X-Request-Id", requestId);
		return headers;
	}

	private String makeSignatureUri(String uri)
	{
		return this.apiPath + uri;
	}

	public String packToken(String url, String body, String deadline, String once) 
	{
		String signature = this.signature(url, body, deadline, once);
		return new StringBuilder(this.accessKey).append(":").append(deadline).append(":").append(once).append(":").append(signature).toString();
	}

	private String signature(String url, String body, String deadline, String once) 
	{
		if (null == body) 
		{
			body = "";
		}
		
		StringBuilder sb = new StringBuilder();
		
		// 此种写法为Java 8 的写法 
		//String data = String.join("\n", url, deadline + "", once, body);
		
		String data = sb.append(url).append("\n").append(deadline+"").append("\n").append(once).append("\n").append(body).toString();
		
		byte[] signature = hmac_sha1(data, this.secretKey);
		
		String sign = new BASE64Encoder().encode(signature);
		
		sign = sign.replace("+", "-").replace("/", "_");
		
		return sign;
	}

	private byte[] hmac_sha1(String value, String key) 
	{
		try 
		{
			// Get an hmac_sha1 key from the raw key bytes
			byte[] keyBytes = key.getBytes();
			SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");

			// Get an hmac_sha1 Mac instance and initialize with the signing key
			Mac mac = Mac.getInstance("HmacSHA1");
			
			mac.init(signingKey);

			byte[] rawHmac = mac.doFinal(value.getBytes());
			
			return rawHmac;
		} 
		catch (Exception e) 
		{
			throw new RuntimeException(e);
		}
	}
	
	
	private String makeRequestId() 
	{
		return System.currentTimeMillis() + this.getMD5(UUID.randomUUID().toString());
	}

	private String makeUrl(String uri) 
	{
		return this.endpoint + uri;
	}

	private String getMD5(String str) 
	{
		try 
		{
			// 生成一个MD5加密计算摘要
			MessageDigest md = MessageDigest.getInstance("MD5");
			// 计算md5函数
			md.update(str.getBytes());
			// digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
			// BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
			return new BigInteger(1, md.digest()).toString(16);
		} 
		catch (Exception e)
		{
			throw new RuntimeException("MD5加密出现错误");
		}
	}

	private String httpBuildQuery(Map<String, String> map) 
	{
		if (null == map || map.isEmpty()) 
		{
			return null;
		}
		String reString = null;
		// 遍历数组形成akey=avalue&bkey=bvalue&ckey=cvalue形式的的字符串
		Iterator<Entry<String, String>> it = map.entrySet().iterator();
		while (it.hasNext()) 
		{
			Map.Entry<String, String> entry = it.next();
			String key = entry.getKey();
			String value = entry.getValue();
			reString += key + "=" + value + "&";
		}
		reString = reString.substring(0, reString.length() - 1);
		// 将得到的字符串进行处理得到目标格式的字符串
		try 
		{
			reString = java.net.URLEncoder.encode(reString, "utf-8");
		} catch (UnsupportedEncodingException e) 
		{
			e.printStackTrace();
		}
		reString = reString.replace("%3D", "=").replace("%26", "&");
		return reString;
	}
	
	public static void main(String[] args) 
	{
		// http://e3c.edusoho.cn/
		// http://e3c.dev.com
		ResourceRestAPIClient client = new ResourceRestAPIClient("test_acess_key", "test_secret_key", "http://e3c.edusoho.cn");
		
//		Map<String, String> params = new HashMap<String, String>();
//		params.put("key", "aaaa");
//		
//		String result = client.doGet("/contents",params,null);
//		System.out.println(result);
//		
//		
//		result = client.doPost("/contents",params,null);
//		System.out.println(result);
		
		String result;
		try {
			long start = System.currentTimeMillis();
			result = client.getResCategories();
			//result = client.getResCataoriesByPOST();
			System.out.println("[耗时]："+(System.currentTimeMillis()-start)+"ms");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			result = "请求异常";
		}
		System.out.println(result);
	}
}
